// -----------------------------------------------------------
// Game Framework
// Copyright © 2024-2024 deadcycle. All rights reserved.
// Homepage: https://deadcycle.gitee.io
// Feedback: deadcycle@163.com
// -----------------------------------------------------------

using System;
using System.Collections.Generic;

using Cysharp.Threading.Tasks;

using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;

using Debug = UnityEngine.Debug;

namespace UIWidget
{
    /// <summary>
    /// 滚动视图扩展控件基类, 复用Cell单元。
    /// </summary>
    [DisallowMultipleComponent, RequireComponent(typeof(RectTransform))]
    public abstract partial class ScrollRectBase : ScrollRect
    {
        #region 静态字段
        private static readonly YieldAwaitable s_WaitForEndOfFrameAwaitable =
            UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);

        private static Rect s_CachedRect;
        private static Vector2 s_CachedSize;
        private static Vector2 s_CachedPosition;
        private static bool s_ApplicationIsQuitting;

        protected const bool k_EnableDebug = false;
        protected const int k_VerticalFlag = 1;
        #endregion

        #region 字段
        /// <summary> 滚动列表单元格的布局方式。 </summary>
        [SerializeField]
        protected LayoutType m_LayoutType = LayoutType.Vertical;

        /// <summary> 未填满视口时, 是否禁止滚动。 </summary>
        [SerializeField]
        private bool m_AutoDisableScrolling;

        /// <summary> 默认对象池初始容量。 </summary>
        [SerializeField]
        protected int m_PoolCapacity = 10;

        /// <summary> 标识如何刷新滚动列表的方式。 </summary>
        private byte m_WillUpdateData = WillUpdateData.None;

        private bool m_Initialized;
        private bool m_DelayTaskRunning;
        private bool m_IsDestroying;

        private readonly Vector3[] m_ViewWorldCorners = new Vector3[4];
        private readonly Vector3[] m_RectCorners = new Vector3[2];
        private Rect m_RefRect;

        /// <summary> 记录边界单元格的索引值。 </summary>
        protected int[] m_CriticalCellIndexes = new int[4];

        protected Transform m_PoolTransform;

        /// <summary> 被管理的Cell单元数据数量。 </summary>
        protected int m_CellDataCount;

        /// <summary> 管理的Cell单元数据。 </summary>
        protected readonly List<CellData> m_ManagedCellDatas =
            new List<CellData>();

        private ObjectPool<CellData> m_CachedDataPool;

        // 自定义函数
        private Func<int> m_CellGetCountFunc;
        private Action<int, CellBase> m_UpdateFunc;
        protected Func<int, RectTransform> m_GetInstanceFunc;
        protected Action<RectTransform> m_RecycleInstanceFunc;
        #endregion

        #region 属性
        public static bool ApplicationIsQuitting { set => s_ApplicationIsQuitting = value; }
        #endregion

        #region 生命周期
        protected override void OnEnable()
        {
            base.OnEnable();

            if (m_WillUpdateData != WillUpdateData.None)
            {
                DelayUpdateData().Forget();
            }
        }

        protected override void OnDisable()
        {
            m_Initialized = false;

            base.OnDisable();
        }

        protected override void OnDestroy()
        {
            ClearInstancePool();
            ClearDataPool();

            m_IsDestroying = true;

            base.OnDestroy();
        }
        #endregion

        #region 设置回调
        /// <summary>
        /// 设置获取Cell数量的回调。
        /// </summary>
        public virtual void SetGetCountFunc(Func<int> cellGetCountFunc)
        {
            if (cellGetCountFunc == null)
            {
                throw new ArgumentNullException(nameof(cellGetCountFunc));
            }

            m_CellGetCountFunc = cellGetCountFunc;
        }

        /// <summary>
        /// 设置获取与回收Cell实例对象的回调。
        /// </summary>
        public void SetInstanceFunc(Func<int, RectTransform> getInstanceFunc,
            Action<RectTransform> recycleInstanceFunc)
        {
            if (getInstanceFunc == null)
            {
                throw new ArgumentNullException(nameof(getInstanceFunc));
            }

            if (recycleInstanceFunc == null)
            {
                throw new ArgumentNullException(nameof(recycleInstanceFunc));
            }

            m_GetInstanceFunc = getInstanceFunc;
            m_RecycleInstanceFunc = recycleInstanceFunc;
        }

        /// <summary>
        /// 设置Cell更新的回调。
        /// </summary>
        public void SetUpdateFunc(Action<int, CellBase> updateFunc)
        {
            if (updateFunc == null)
            {
                throw new ArgumentNullException(nameof(updateFunc));
            }

            m_UpdateFunc = updateFunc;
        }
        #endregion

        #region 刷新滚动视图相关
        /// <summary>
        /// 刷新滚动视图。
        /// 1. 此方式用于全部重新计算Cell布局, 当一个索引对应的Cell的前后Size发生变化时, 应该使用此接口刷新
        /// 2. 其他情况可以使用UpdateDataIncrementally, 提高性能
        /// </summary>
        /// <param name="immediately">立即刷新。</param>
        public void UpdateData(bool immediately = true)
        {
            if (immediately)
            {
                m_WillUpdateData |= WillUpdateData.NeedUpdateAndReset;
                InternalUpdateData();
            }
            else
            {
                if (m_WillUpdateData == WillUpdateData.None && IsActive())
                {
                    m_WillUpdateData |= WillUpdateData.NeedUpdateAndReset;

                    // UniTask 延迟刷新
                    DelayUpdateData().Forget();
                }
                else
                {
                    // 正在刷新中, 或者非active状态, 仅标识即可
                    m_WillUpdateData |= WillUpdateData.NeedUpdateAndReset;
                }
            }
        }

        /// <summary>
        /// 增量方式刷新滚动视图(需要注意, 如果前一次以非增量方式刷新滚动视图未完成, 保证安全性, 必须以非增量的方式完成刷新)。
        /// </summary>
        /// <param name="immediately">立即刷新。</param>
        public void UpdateDataIncrementally(bool immediately = true)
        {
            if (immediately)
            {
                m_WillUpdateData |= WillUpdateData.NeedUpdate;
                InternalUpdateData();
            }
            else
            {
                if (m_WillUpdateData == WillUpdateData.None && IsActive())
                {
                    m_WillUpdateData |= WillUpdateData.NeedUpdate;
                    DelayUpdateData().Forget();
                }
                else
                {
                    m_WillUpdateData |= WillUpdateData.NeedUpdate;
                }
            }
        }

        /// <summary>
        /// 局部更新某个索引Cell单元。
        /// </summary>
        /// <param name="index">索引。</param>
        public void UpdateDataAtIndex(int index)
        {
            if (m_CellDataCount == 0)
            {
                return;
            }

            if (index < -m_CellDataCount || index >= m_CellDataCount)
            {
                Debug.LogError("Scroll index out of range.");

                return;
            }

            // 全局刷新中或者全局延迟刷新中, 交由全局统一刷新
            if (m_WillUpdateData != WillUpdateData.None)
            {
                return;
            }

            // 支持负数索引, 反向指定索引
            if (index < 0)
            {
                index += m_CellDataCount;
            }

            index = Mathf.Clamp(index, 0, m_CellDataCount - 1);

            EnsureCellRect(index);

            // 是否在可视区域内
            bool shouldShow = ShouldCellSeenAtIndex(index);

            if (!shouldShow)
            {
                // 不在可视区域内, 忽略此次刷新
                return;
            }

            // 当前Cell数据是否关联了Cell对象
            bool hasCell = m_ManagedCellDatas[index].Target != null;

            // 偶发异常情况, 仅做安全性的校验, 索引应当在边界隐藏Cell的索引范围呢
            if (index < m_CriticalCellIndexes[CriticalType.UpToHide] ||
                index > m_CriticalCellIndexes[CriticalType.DownToHide] ||
                !hasCell
            )
            {
                Debug.LogError("Scroll index out of range.");

                return;
            }

            UpdateCellAtIndex(index);
        }

        /// <summary>
        /// 滚动到某个索引元素(瞬间效果, 非动画)。
        /// </summary>
        /// <param name="index">元素索引值。</param>
        public virtual void ScrollToIndex(int index)
        {
            if (m_CellDataCount == 0)
            {
                return;
            }

            if (index < -m_CellDataCount || index >= m_CellDataCount)
            {
                Debug.LogError("Scroll index out of range.");

                return;
            }

            // 支持负数索引, 反向指定索引
            if (index < 0)
            {
                index += m_CellDataCount;
            }

            index = Mathf.Clamp(index, 0, m_CellDataCount - 1);

            EnsureCellRect(index);

            Rect rect = m_ManagedCellDatas[index].Rect;

            bool isVertical = ((int)m_LayoutType & k_VerticalFlag) == 1;

            // 计算出滚动的位置在滚动视图内的归一化坐标
            if (isVertical)
            {
                float normalized;
                float range = content.sizeDelta.y - m_RefRect.height;

                if (range <= 0)
                {
                    normalized = 1;
                }
                else
                {
                    // 当滑动方式上, 元素的尺寸比视口尺寸要大时, 采用元素顶部与视口顶部对齐的方式
                    if (rect.height > m_RefRect.height)
                    {
                        normalized = Mathf.Clamp(-rect.yMax, 0, range) / range;
                    }
                    // 元素可以在视口内完整展示出来时, 采用元素中心与视口中心对齐的方式
                    else
                    {
                        normalized =
                            Mathf.Clamp(-rect.yMax - (m_RefRect.height - rect.height) * 0.5f, 0,
                                range) /
                            range;
                    }

                    normalized = 1 - Mathf.Clamp01(normalized);
                }

                SetNormalizedPosition(normalized, 1);
            }
            else
            {
                float normalized;
                float range = content.sizeDelta.x - m_RefRect.width;

                if (range <= 0)
                {
                    normalized = 0;
                }
                else
                {
                    // 当滑动方式上, 元素的尺寸比视口尺寸要大时, 采用元素左侧部与视口左侧对齐的方式
                    if (rect.width > m_RefRect.width)
                    {
                        normalized = Mathf.Clamp(rect.xMin, 0, range) / range;
                    }
                    else
                        // 元素可以在视口内完整展示出来时, 采用元素中心与视口中心对齐的方式
                    {
                        normalized = Mathf.Clamp(rect.xMin - (m_RefRect.width - rect.width) * 0.5f,
                                0, range) /
                            range;
                    }

                    normalized = Mathf.Clamp01(normalized);
                }

                SetNormalizedPosition(normalized, 0);
            }
        }
        #endregion

        #region 内部接口
        #region 内部刷新相关
        /// <summary>
        /// 异步延迟刷新滚动视图。
        /// </summary>
        private async UniTaskVoid DelayUpdateData()
        {
            // 避免启动多个异步任务刷新视图
            if (m_DelayTaskRunning)
            {
                return;
            }

            m_DelayTaskRunning = true;

            await s_WaitForEndOfFrameAwaitable;

            // 正在销毁, 异步任务需要结束了
            if (m_IsDestroying)
            {
                return;
            }

            InternalUpdateData();

            m_DelayTaskRunning = false;
        }

        /// <summary>
        /// 内部真实刷新滚动视图逻辑。
        /// </summary>
        private void InternalUpdateData()
        {
            if (!IsActive())
            {
                m_WillUpdateData |= WillUpdateData.NeedUpdateAndReset;

                return;
            }

            // 启动延迟刷新调用后, 等待刷新时, 强制立即刷新了, 不需要刷新了
            if (m_WillUpdateData == WillUpdateData.None)
            {
                return;
            }

            if (!m_Initialized)
            {
                Initialize();
            }

            CheckDataCountChange();

            IterateCells();

            // 刷新完成, 清除标识
            m_WillUpdateData = WillUpdateData.None;
        }

        /// <summary>
        /// 检查Cell数量变化。
        /// </summary>
        protected virtual void CheckDataCountChange()
        {
            int newDataCount = 0;

            if (m_CellGetCountFunc != null)
            {
                try

                {
                    newDataCount = m_CellGetCountFunc();
                }
                catch (Exception e)

                {
                    Debug.LogException(e);
                }
            }

            // 布局受到改变, 需重新计算Rect区域
            bool needReset = (m_WillUpdateData & WillUpdateData.Reset) != 0;

            if (newDataCount == m_ManagedCellDatas.Count)
            {
                // 数量没有发生变化
                if (needReset)
                {
                    foreach (CellData cellData in m_ManagedCellDatas)
                    {
                        cellData.RectIsDirty = true;
                    }
                }
            }
            else
            {
                if (m_ManagedCellDatas.Count < newDataCount)
                {
                    if (needReset)
                    {
                        // 重置所有Cell单元数据
                        foreach (CellData cellData in m_ManagedCellDatas)
                        {
                            cellData.RectIsDirty = true;
                        }
                    }

                    // 新增缺少的Cell单元数据
                    for (int i = 0, count = newDataCount - m_ManagedCellDatas.Count; i < count; i++)
                    {
                        m_ManagedCellDatas.Add(GetCellData());
                    }
                }
                else
                {
                    // 如果Cell数量减少, 保留空位, 避免产生GC
                    for (int i = 0, count = m_ManagedCellDatas.Count; i < count; i++)
                    {
                        if (i < newDataCount)
                        {
                            // 1. 布局受到改变, 重新计算Rect 2. 最后一个Cell需要标记为Dirty, 驱动更新content尺寸  
                            if (needReset || i == newDataCount - 1)
                            {
                                m_ManagedCellDatas[i].RectIsDirty = true;
                            }
                        }
                        else
                        {
                            // Cell单元数据关联一个实际Cell对象, 解除关联, 回收后Cell对象, 进行复用
                            if (m_ManagedCellDatas[i].Target != null)
                            {
                                RecycleCellInstance(m_ManagedCellDatas[i].Target);
                            }

                            // 超出的Cell数据, 进行清理
                            m_ManagedCellDatas[i].Clear();
                        }
                    }
                }
            }

            m_CellDataCount = newDataCount;
        }

        /// <summary>
        /// 迭代所有的Cell单元, 来处理Cell单元是否可见, 并完成可见Cell的渲染更新。
        /// </summary>
        private void IterateCells()
        {
            int firstIndex = -1, lastIndex = -1; // 可见Cell的索引区间

            for (int i = 0; i < m_CellDataCount; i++)
            {
                // 当前Cell数据是否关联了Cell对象
                bool hasCell = m_ManagedCellDatas[i].Target != null;
                // 当前Cell是否可见
                bool shouldShow = ShouldCellSeenAtIndex(i);

                if (shouldShow)
                {
                    // 记录首个可见Cell索引
                    if (firstIndex == -1)
                    {
                        firstIndex = i;
                    }

                    // 当前Cell可见时, 最后Cell索引更新为当前Cell的索引
                    lastIndex = i;
                }

                switch (shouldShow, hasCell)
                {
                    case (false, false):
                        // Cell不可见, Cell对象也不存在, 不需要任何处理
                        break;

                    case (true, true):
                        // Cell可见, Cell对象也存在, 刷新Cell
                        UpdateCellAtIndex(i);

                        break;

                    case (false, true):
                        // Cell不可见, 但Cell对象存在, 需要回收Cell对象进行复用, 并清除Cell数据
                        RecycleCellInstance(m_ManagedCellDatas[i].Target);
                        m_ManagedCellDatas[i].ClearAssociatedData();

                        break;
                    case (true, false):
                        // Cell可见, 但Cell对象不存在, 需要获取Cell对象, 刷新Cell, 关联到Cell数据上
                        RectTransform cellObject = GetCellInstance(i);

                        if (cellObject == null)
                        {
                            throw new Exception($"Get cell object is null. Index: {i}");
                        }

                        SetInstanceForIndex(i, cellObject);
                        UpdateCellAtIndex(i);

                        break;
                }
            }

            m_CriticalCellIndexes[CriticalType.UpToHide] = firstIndex;
            m_CriticalCellIndexes[CriticalType.DownToHide] = lastIndex;
            m_CriticalCellIndexes[CriticalType.UpToShow] = Mathf.Max(firstIndex - 1, 0);

            m_CriticalCellIndexes[CriticalType.DownToShow] =
                Mathf.Min(lastIndex + 1, m_CellDataCount - 1);

            // Debug.Log(
            //     $"迭代所有的Cell完成后, 索引分别为 : {firstIndex}, {lastIndex}, {Mathf.Max(firstIndex - 1, 0)}, {Mathf.Min(lastIndex + 1, m_CellDataCount - 1)}");
        }

        /// <summary>
        /// Cell是否可以被看见, 在可见区域的视口范围里。
        /// </summary>
        /// <param name="index">Cell的索引。</param>
        /// <returns>是否可见。</returns>
        private bool ShouldCellSeenAtIndex(int index)
        {
            if (index < 0 || index >= m_CellDataCount)
            {
                return false;
            }

            EnsureCellRect(index);

            Vector2 contentPosition = content.anchoredPosition;

            s_CachedRect.Set(m_RefRect.x - contentPosition.x, m_RefRect.y - contentPosition.y,
                m_RefRect.width, m_RefRect.height);

            return s_CachedRect.Overlaps(m_ManagedCellDatas[index].Rect);
        }

        /// <summary>
        /// 确保Cell单元的Rect数据是正确最新的。
        /// </summary>
        /// <param name="index">Cell的索引。</param>
        private void EnsureCellRect(int index)
        {
            if (!m_ManagedCellDatas[index].RectIsDirty)
            {
                return;
            }

            // ScrollRect 滚动坐标基准点, 以content左上角为(0, 0)点, 以下坐标的计算以此为标准
            // 确保首个Cell的Rect是正确的
            CellData firstCellData = m_ManagedCellDatas[0];

            if (firstCellData.RectIsDirty)
            {
                // 更新首个Cell的Size
                Vector2 firstCellSize = GetCellSize(0);
                /*
                 *           width
                 *      * ---------- *
                 *      |    rect    |        height
                 *   x, y ---------- *
                 */

                firstCellData.SetRect(0, -firstCellSize.y, firstCellSize.x, firstCellSize.y);
            }

            // 找到目标Cell的之前, Rect是正确且最靠近的Cell索引
            int nearestIndex = 0;

            for (int i = index; i >= 0; --i)
            {
                if (!m_ManagedCellDatas[i].RectIsDirty)
                {
                    nearestIndex = i;

                    break;
                }
            }

            // 得到该Cell, 正确的Rect数据
            Rect nearestCellRect = m_ManagedCellDatas[nearestIndex].Rect;
            // 获取rect左上角坐标
            s_CachedPosition.Set(nearestCellRect.xMin, nearestCellRect.yMax);
            s_CachedSize.Set(nearestCellRect.width, nearestCellRect.height);
            // 计算后续rect左上角坐标
            CalcNextCellLeftTop();

            for (int i = nearestIndex + 1; i <= index; i++)
            {
                Vector2 size = GetCellSize(i);

#if UNITY_EDITOR
                if (size.x <= 0 || size.y <= 0)
                {
                    Debug.LogError(
                        "Get cell size is invalid. Both x and y should be greater than 0. (Cell的size分量必须都大于0)");
                }
#endif

                m_ManagedCellDatas[i]
                    .SetRect(s_CachedPosition.x, s_CachedPosition.y - size.y, size.x, size.y);

                s_CachedSize.Set(size.x, size.y);
                CalcNextCellLeftTop();
            }

            Vector2 sizeDelta =
                new Vector2(Mathf.Abs(s_CachedPosition.x), Mathf.Abs(s_CachedPosition.y));

            switch (m_LayoutType)
            {
                case LayoutType.Vertical:
                    sizeDelta.x = m_RefRect.width;

                    break;

                case LayoutType.Horizontal:
                    sizeDelta.y = m_RefRect.height;

                    break;

                // 网格布局时, 需要额外进行计算 
                case LayoutType.VerticalThenHorizontal:
                    sizeDelta.y = m_RefRect.height;

                    if (s_CachedPosition.y != 0)
                    {
                        sizeDelta.x += s_CachedSize.x;
                    }

                    break;

                case LayoutType.HorizontalThenVertical:
                    sizeDelta.x = m_RefRect.width;

                    if (s_CachedPosition.x != 0)
                    {
                        sizeDelta.y += s_CachedSize.y;
                    }

                    break;
            }

            content.sizeDelta = sizeDelta;

            if (m_AutoDisableScrolling)
            {
                OnContentSizeChanged();
            }
        }

        /// <summary>
        /// 获取Cell的Size。
        /// </summary>
        /// <param name="index">Cell的索引。</param>
        protected abstract Vector2 GetCellSize(int index);

        /// <summary>
        /// 根据Cell的滑动, 计算下一个Cell的左上角坐标。 
        /// </summary>
        private void CalcNextCellLeftTop()
        {
            switch (m_LayoutType)
            {
                case LayoutType.Vertical:
                    s_CachedPosition.y -= s_CachedSize.y;

                    break;

                case LayoutType.Horizontal:
                    s_CachedPosition.x += s_CachedSize.x;

                    break;

                // 这里需要注意, 网格布局不支持不定长尺寸, 这时的计算依赖定长逻辑
                case LayoutType.VerticalThenHorizontal:
                    s_CachedPosition.y -= s_CachedSize.y;

                    if (s_CachedPosition.y - s_CachedSize.y < -m_RefRect.height)
                    {
                        s_CachedPosition.y = 0;
                        s_CachedPosition.x += s_CachedSize.x;
                    }

                    break;

                case LayoutType.HorizontalThenVertical:
                    s_CachedPosition.x += s_CachedSize.x;

                    if (s_CachedPosition.x + s_CachedSize.x > m_RefRect.width)
                    {
                        s_CachedPosition.x = 0;
                        s_CachedPosition.y -= s_CachedSize.y;
                    }

                    break;
            }
        }

        /// <summary>
        /// 将复用Cell实例分配给对应索引。 
        /// </summary>
        /// <param name="index">索引值。</param>
        /// <param name="cellInstance">复用Cell实例。</param>
        protected virtual void SetInstanceForIndex(int index, RectTransform cellInstance)
        {
            if (cellInstance == null)
            {
                throw new Exception($"Get cell object is null. Index: {index}");
            }

            cellInstance.SetParent(content, false);
            m_ManagedCellDatas[index].Target = cellInstance;
        }

        /// <summary>
        /// 根据索引刷新对应Cell单元的显示。 
        /// </summary>
        /// <param name="index">Cell索引。</param>
        private void UpdateCellAtIndex(int index)
        {
            if (index < 0 || index >= m_CellDataCount)
            {
                return;
            }

            RectTransform rectTransform = m_ManagedCellDatas[index].Target;

            if (rectTransform == null)
            {
                return;
            }

            CellBase cellScript = m_ManagedCellDatas[index].CellScript;

            if (m_UpdateFunc != null && cellScript != null)
            {
                try
                {
                    m_UpdateFunc(index, cellScript);
                }
                catch (Exception e)
                {
                    Debug.LogException(e);

                    throw;
                }
            }

            if (m_ManagedCellDatas[index].RectIsDirty)
            {
                EnsureCellRect(index);
            }

            Rect rect = m_ManagedCellDatas[index].Rect;
            rectTransform.localPosition = rect.position;
            rectTransform.sizeDelta = rect.size;
        }

        /// <summary>
        /// 迭代边界Cell单元, 来处理滑动过程中边界Cell单元是否可见, 并完成可见Cell的渲染更新。
        /// </summary>
        private void IterateCriticalCells()
        {
            bool dirty = true;

            // 不停扫描, 直至4个边界Cell单元全部同步为最新状态
            while (dirty)
            {
                dirty = false;

                for (byte i = CriticalType.UpToHide; i <= CriticalType.DownToShow; i++)
                {
                    if (i == CriticalType.UpToHide || i == CriticalType.DownToHide)
                    {
                        // 检查是否有Cell离开了可见区域
                        dirty = dirty || CheckAndHideCriticalCell(i);
                    }
                    else
                    {
                        // 检查是否有Cell进入了可见区域
                        dirty = dirty || CheckAndShowCriticalCell(i);
                    }
                }
            }
        }

        /// <summary>
        /// 检查是否有可见的Cell离开了可见区域, 回收Cell, 更新边界Cell单元格的索引。
        /// </summary>
        private bool CheckAndHideCriticalCell(byte criticalType)
        {
            RectTransform rectTransform = GetCriticalCellInstance(criticalType);

            if (rectTransform == null)
            {
                // 无Cell对象, 即不可见 
                return false;
            }

            int cellIndex = m_CriticalCellIndexes[criticalType];

            if (ShouldCellSeenAtIndex(cellIndex))
            {
                return false;
            }

            // Cell离开可见区域了

            // 回收Cell对象, 但不清除Rect信息
            RecycleCellInstance(m_ManagedCellDatas[cellIndex].Target);
            m_ManagedCellDatas[cellIndex].ClearAssociatedData();

            if (criticalType == CriticalType.UpToHide)
            {
                // 最上面的Cell隐藏了, 更新顶部的两个Cell索引
                m_CriticalCellIndexes[CriticalType.UpToShow] = Mathf.Max(cellIndex,
                    m_CriticalCellIndexes[CriticalType.UpToShow]);
                m_CriticalCellIndexes[CriticalType.UpToHide]++;
            }
            else
            {
                // 最下面的Cell隐藏了, 更新底部的两个Cell索引。
                m_CriticalCellIndexes[CriticalType.DownToShow] = Mathf.Min(cellIndex,
                    m_CriticalCellIndexes[CriticalType.DownToShow]);

                m_CriticalCellIndexes[CriticalType.DownToHide]--;
            }

            // 保证索引不会越界
            m_CriticalCellIndexes[criticalType] = Mathf.Clamp(m_CriticalCellIndexes[criticalType],
                0,
                m_CellDataCount - 1);

            // 偶发情况, 拖拽超出一屏
            if (m_CriticalCellIndexes[CriticalType.UpToHide] >
                m_CriticalCellIndexes[CriticalType.DownToHide])
            {
                IterateCells();

                return false;
            }

            return true;
        }

        /// <summary>
        /// 检查是否有Cell进入了可见区域, 渲染Cell, 更新边界Cell单元格的索引
        /// </summary>
        private bool CheckAndShowCriticalCell(byte criticalType)
        {
            RectTransform rectTransform = GetCriticalCellInstance(criticalType);

            if (rectTransform != null)
            {
                return false;
            }

            int cellIndex = m_CriticalCellIndexes[criticalType];

            if (!ShouldCellSeenAtIndex(cellIndex))
            {
                return false;
            }

            // Cell进入可见区域了
            RectTransform cellObject = GetCellInstance(cellIndex);

            if (cellObject == null)
            {
                throw new Exception($"Get cell object is null. Index: {cellIndex}");
            }

            SetInstanceForIndex(cellIndex, cellObject);

            // 渲染Cell
            UpdateCellAtIndex(cellIndex);

            if (criticalType == CriticalType.UpToShow)
            {
                // 最上面的Cell显示了, 更新顶部的两个Cell索引
                m_CriticalCellIndexes[CriticalType.UpToHide] = Mathf.Min(cellIndex,
                    m_CriticalCellIndexes[CriticalType.UpToHide]);
                m_CriticalCellIndexes[CriticalType.UpToShow]--;
            }
            else
            {
                // 最下面的Cell显示了, 更新底部的两个Cell索引。
                m_CriticalCellIndexes[CriticalType.DownToHide] = Mathf.Max(cellIndex,
                    m_CriticalCellIndexes[CriticalType.DownToHide]);
                m_CriticalCellIndexes[CriticalType.DownToShow]++;
            }

            // 保证索引不会越界
            m_CriticalCellIndexes[criticalType] = Mathf.Clamp(m_CriticalCellIndexes[criticalType],
                0,
                m_CellDataCount - 1);

            // 偶发情况, 拖拽超出一屏
            if (m_CriticalCellIndexes[CriticalType.UpToShow] >=
                m_CriticalCellIndexes[CriticalType.DownToShow])
            {
                IterateCells();

                return false;
            }

            return true;
        }

        /// <summary>
        /// 获取记录的边界单元对象。
        /// </summary>
        private RectTransform GetCriticalCellInstance(byte criticalType)
        {
            int cellIndex = m_CriticalCellIndexes[criticalType];

            if (cellIndex < 0 || cellIndex >= m_CellDataCount)
            {
                return null;
            }

            return m_ManagedCellDatas[cellIndex].Target;
        }
        #endregion

        #region 初始化
        /// <summary>
        /// 初始化必要参数。
        /// </summary>
        private void Initialize()
        {
            m_Initialized = true;

            // 根据Cell单元布局方式, 控制ScrollRect滚动方向
            bool isVertical = ((int)m_LayoutType & k_VerticalFlag) == 1;
            vertical = isVertical;
            horizontal = !isVertical;

            // 修改content
            content.pivot = Vector2.up;
            content.anchorMin = Vector2.up;
            content.anchorMax = Vector2.up;
            content.anchoredPosition = Vector2.zero;

            // 初始化Cell单元缓存池
            InitPool();

            // 获取视口矩形转换到content坐标系下的区域范围
            UpdateRefRect();
        }

        /// <summary>
        /// 获取视口矩形转换到content坐标系下的区域范围。
        /// </summary>
        private void UpdateRefRect()
        {
            // 如果正在重新构建布局, 不能更新Canvas, 可能会引起Canvas的重排, 导致循环递归
            if (!CanvasUpdateRegistry.IsRebuildingLayout())
            {
                // 更新画布, 获取最新布局信息
                Canvas.ForceUpdateCanvases();
            }

            /*
             *  WorldCorners
             *
             *    1 ------- 2
             *    |         |
             *    |         |
             *    0 ------- 3
             *
             */
            // refRect是在Content节点下的 viewport的 rect
            viewRect.GetWorldCorners(m_ViewWorldCorners);
            m_RectCorners[0] = content.InverseTransformPoint(m_ViewWorldCorners[0]);
            m_RectCorners[1] = content.InverseTransformPoint(m_ViewWorldCorners[2]);

            m_RefRect = new Rect(m_RectCorners[0], m_RectCorners[1] - m_RectCorners[0]);
        }
        #endregion

        #region 对象池相关
        private void InitPool()
        {
            InitPoolRoot();
            InitInstancePool();
            InitDataPool();
        }

        #region 实例对象池
        /// <summary>
        /// 初始化Cell单元缓存池对象。
        /// </summary>
        private void InitPoolRoot()
        {
            if (m_PoolTransform != null)
            {
                return;
            }

            // 创建对象池节点
            GameObject poolObject = new GameObject("Pool");

            // 隐藏不可见节点方案, 代码暂时保留 
            // poolObject.SetActive(false);

            // 禁用画布组件的方式更加高效, 可以保留VBO数据, 但需要注意一点是Cell单元上的脚本, 注册了`Update`函数, 会保持运行状态
            Canvas canvas = poolObject.AddComponent<Canvas>();
            canvas.enabled = false;

            m_PoolTransform = poolObject.transform;
            m_PoolTransform.SetParent(transform, false);
        }

        /// <summary>
        /// 初始化实例对象池。
        /// </summary>
        protected abstract void InitInstancePool();

        /// <summary>
        /// 清理实例对象池。
        /// </summary>
        protected abstract void ClearInstancePool();

        /// <summary>
        /// 对象池获取对象时, 需要创建新的对象时回调。
        /// </summary>
        protected virtual RectTransform OnPoolCreateInstance()
        {
            throw new InvalidOperationException();
        }

        /// <summary>
        /// 对象进入对象池回收时回调。
        /// </summary>
        protected virtual void OnPoolReleaseInstance(RectTransform releaseObject)
        {
            releaseObject.SetParent(m_PoolTransform, false);
        }

        /// <summary>
        /// 对象进入对象池回收, 但对象池已满, 对象被销毁时回调。
        /// </summary>
        protected virtual void OnPoolDestroyInstance(RectTransform destroyObject)
        {
            // 注意, 需要外部处理Unity生命周期 `OnApplicationQuit` 标记此变量
            if (s_ApplicationIsQuitting)
            {
                return;
            }

            Destroy(destroyObject.gameObject);
        }

        /// <summary>
        /// 如何获取Cell对象实例。
        /// </summary>
        /// <param name="index">索引。</param>
        protected abstract RectTransform GetCellInstance(int index);

        /// <summary>
        /// 回收Cell对象。
        /// </summary>
        /// <param name="instance">Cell对象。</param>
        protected abstract void RecycleCellInstance(RectTransform instance);

        /// <summary>
        /// 从自定义函数中来获取Cell对象实例。
        /// </summary>
        /// <param name="index">索引。</param>
        /// <param name="cellInstance">cell对象实例。</param>
        /// <returns>是否使用自定义方式处理。</returns>
        protected bool GetCellInstanceCustom(int index, ref RectTransform cellInstance)
        {
            if (m_GetInstanceFunc == null)
            {
                return false;
            }

            try
            {
                cellInstance = m_GetInstanceFunc(index);

                if (cellInstance != null)
                {
                    cellInstance.anchorMin = Vector2.up;
                    cellInstance.anchorMax = Vector2.up;
                    cellInstance.pivot = Vector2.zero;
                }
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            return true;
        }

        /// <summary>
        /// 使用自定义函数中来回收Cell对象实例。
        /// </summary>
        /// <param name="instance">Cell对象实例。</param>
        protected bool RecycleInstanceCustom(RectTransform instance)
        {
            if (m_RecycleInstanceFunc == null)
            {
                return false;
            }

            try
            {
                m_RecycleInstanceFunc(instance);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            return true;
        }
        #endregion

        #region 数据对象池
        /// <summary>
        /// 初始化Cell单元数据对象池。
        /// </summary>
        private void InitDataPool()
        {
            if (m_CachedDataPool != null)
            {
                return;
            }

#if UNITY_EDITOR
            m_CachedDataPool = new ObjectPool<CellData>(OnPoolCreateData,
                collectionCheck: k_EnableDebug, defaultCapacity: m_PoolCapacity);
#else
            m_CachedDataPool = new ObjectPool<CellData>(OnPoolCreateData,
                collectionCheck: false, defaultCapacity: m_PoolCapacity);
#endif
        }

        private CellData OnPoolCreateData()
        {
            return new CellData();
        }

        /// <summary>
        /// 获取Cell单元数据。
        /// </summary>
        private CellData GetCellData()
        {
            CellData cellData = m_CachedDataPool.Get();
            cellData.Initialize();

            return cellData;
        }

        private void ClearDataPool()
        {
            if (m_CachedDataPool == null)
            {
                return;
            }

            m_CachedDataPool.Clear();
            m_CachedDataPool = null;
        }
        #endregion
        #endregion

        #region 滑动定位相关
        /// <summary>
        /// >Set the horizontal or vertical scroll position as a value between 0 and 1, with 0 being at the left or at the bottom.
        /// </summary>
        /// <param name="value">The position to set, between 0 and 1.</param>
        /// <param name="axis">The axis to set: 0 for horizontal, 1 for vertical.</param>
        protected override void SetNormalizedPosition(float value, int axis)
        {
            base.SetNormalizedPosition(value, axis);

            // 正在刷新中
            if (m_WillUpdateData != WillUpdateData.None)
            {
                return;
            }

            IterateCells();
        }

        /// <summary>
        /// Sets the anchored position of the content.
        /// </summary>
        protected override void SetContentAnchoredPosition(Vector2 position)
        {
            base.SetContentAnchoredPosition(position);

            if (m_WillUpdateData != WillUpdateData.None)
            {
                return;
            }

            IterateCriticalCells();
        }
        #endregion

        #region 未填满视口时, 是否禁止滚动
        private void OnContentSizeChanged()
        {
            if (!m_AutoDisableScrolling)
            {
                return;
            }

            // 后续逻辑依赖完成初始化
            if (!m_Initialized)
            {
                return;
            }

            bool isVertical = ((int)m_LayoutType & k_VerticalFlag) == 1;

            if (isVertical)
            {
                movementType = content.sizeDelta.y <= m_RefRect.height
                    ? MovementType.Clamped
                    : MovementType.Elastic;
            }
            else
            {
                movementType = content.sizeDelta.x <= m_RefRect.width
                    ? MovementType.Clamped
                    : MovementType.Elastic;
            }
        }
        #endregion
        #endregion
    }
}